package com.awesome.auth.service.authentication;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;

/***
 *                     .::::.
 *                   .::::::::.
 *                  :::::::::::    佛主保佑、永无Bug
 *              ..:::::::::::'
 *            '::::::::::::'
 *              .::::::::::
 *         '::::::::::::::..
 *              ..::::::::::::.
 *           ``::::::::::::::::
 *             ::::``:::::::::'        .:::.
 *            ::::'   ':::::'       .::::::::.
 *         .::::'      ::::     .:::::::'::::.
 *        .:::'       :::::  .:::::::::' ':::::.
 *       .::'        :::::.:::::::::'      ':::::.
 *      .::'         ::::::::::::::'         ``::::.
 *  ...:::           ::::::::::::'              ``::.
 * ```` ':.          ':::::::::'                  ::::..
 *                   '.:::::'                    ':'````..
 * @version: V1.0
 * @author: qxw
 * @description: 拦截请求
 * 通过AbstractAuthenticationProcessingFilter向Web应用向基于HTTP、浏览器的请求提供身份验证服务的
 *  *
 * @data: 2020年04月10日 14:53
 * @ModifyDate: 2020年04月10日 14:53
 **/
public class CustomAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    private static final String AUTH_TYPE = "auth_type";
    private static final String USERNAME = "username";
    /**
     * oauth2支持4种授权模式：
     * 密码模式（resource owner password credentials）、
     * 授权码模式（authorization code）、
     * 简化模式（implicit）
     * 客户端模式（client credentials）。---这种模式直接根据client的id和密钥即可获取token，无需用户参与
     **/
    private static final String CREDENTIALS = "credentials";
    private static final String OAUTH_TOKEN_URL = "/oauth/custom/token";
    private static final String HTTP_METHOD_POST = "POST";

    /**
     * 强制的只对POST请求应用
     **/
    public CustomAuthenticationProcessingFilter() {
        super(new AntPathRequestMatcher(OAUTH_TOKEN_URL, "POST"));
    }


    /**
     *重写了attemptAuthentication身份验证入口方法
     **/
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        /**
         * 用于获得客户端向服务器端传送数据的方法（如 get、post、header、trace 等）是否为Post
         * 非POST请求时，给予提示-不支持身份验证方法
         **/
        if (!HTTP_METHOD_POST.equals(request.getMethod().toUpperCase())){
           throw  new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }


        /***
         **
          * 1)在从请求中获取表单提交的用户名和密码字段便会赋值到这两个属性中。
          * 2)而重写attemptAuthentication的方法主要是完了构建一个
          * 3)通过CustomAuthenticationToken实例化了认证 (Authentication)接口，继而按照流程，将其传递给AuthenticationManager调用身份验证核心完成相关工作。
          **/
        AbstractAuthenticationToken authRequest = new CustomAuthenticationToken(
                request.getParameter(AUTH_TYPE), request.getParameter(USERNAME), request.getParameter(CREDENTIALS),
                request.getParameterMap(),new ArrayList<>()
        );

        this.setDetails(request, authRequest);

    // Authentication接口的实现类CustomAuthenticationToken
    // 通过AuthenticationManager提供的验证方法作为参数被传递到了身份验证的核心组件中。
    return this.getAuthenticationManager().authenticate(authRequest);
}

    protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

}
